home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / misc / volume9 / proft.ms < prev    next >
Encoding:
Text File  |  1989-11-26  |  40.8 KB  |  1,127 lines

  1. Newsgroups: comp.sources.misc
  2. subject: v09i009: exec profiler for MSDOS
  3. from: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)
  4. Reply-To: bl@infovox.se (Bj|rn Larsson)
  5.  
  6. Posting-number: Volume 9, Issue 9
  7. Submitted-by: bl@infovox.se (Bj|rn Larsson)
  8. Archive-name: proft.ms
  9.  
  10. This is an execution time profiler for DOS and TurboC/MicroSoft C. Tested
  11. under both the compilers under PCDOS 3.30.
  12.  
  13. # ----------------------------- cut here -----------------------------
  14. #! /bin/sh
  15. # This is a shell archive. Remove anything before the `cut' line,
  16. # then unpack by saving it into a file and typing `sh file'. The
  17. # archive ends by exit(0), so don't worry about trailing junk.
  18. # Contents:
  19. #   makefile
  20. #   makefile.msc
  21. #   profile.c
  22. #   profile.man
  23. #   README
  24. # Wrapped by USER@MS-DOS --- Tue Nov 21 21:23:30 1989
  25. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  26. if test -f makefile -a "${1}" != "-c" ; then 
  27.   echo Will not over-write existing file \"makefile\"
  28. else
  29. echo Extracting - \"makefile\"
  30. sed "s/^X//" >makefile <<'END_OF_makefile'
  31. X#################################################################
  32. X# Makefile for PROFILE, Turbo-C 2.0 version.            #
  33. X# Revised:                        890904    #
  34. X#################################################################
  35. X
  36. XCC=tcc
  37. XCFLAGS=-ml -DTRC_2_0=1 -w -w-pro -w-use -w-par -c
  38. XLINK=tlink
  39. XLFLAGS=/m/c
  40. XLIBDIR=\trc\lib
  41. X
  42. Xdefault:    proft.exe
  43. X
  44. Xproft.exe:    proft.obj
  45. X    $(LINK) $(LFLAGS) $(LIBDIR)\c0l.obj proft.obj,proft.x,\
  46. X    proft.map,$(LIBDIR)\emu.lib $(LIBDIR)\mathl.lib $(LIBDIR)\cl.lib
  47. X    exepack proft.x proft.exe
  48. X    rm -il proft.x
  49. X
  50. Xproft.obj:    profile.c
  51. X    $(CC) $(CFLAGS) -oproft.obj profile.c
  52. END_OF_makefile
  53. if test 572 -ne `wc -c <makefile`; then
  54.     echo \"makefile\" unpacked with wrong size!
  55. fi
  56. # end of overwriting check
  57. fi
  58. if test -f makefile.msc -a "${1}" != "-c" ; then 
  59.   echo Will not over-write existing file \"makefile.msc\"
  60. else
  61. echo Extracting - \"makefile.msc\"
  62. sed "s/^X//" >makefile.msc <<'END_OF_makefile.msc'
  63. X#################################################################
  64. X# Makefile for PROFILE, MicroSoft C 5.1 version.        #
  65. X# Revised:                        890902    #
  66. X#################################################################
  67. X
  68. XCC=cl
  69. XCFLAGS=-Ml -DMSC_5_1=1 -W2 -c
  70. XLINK=link
  71. XLFLAGS=/MAP /EXEPACK
  72. XLIBDIR=\msc\lib
  73. X
  74. Xdefault:    profm.exe
  75. X
  76. Xprofm.exe:    profm.obj
  77. X    $(LINK) $(LFLAGS) profm.obj,profm.exe,profm.map;
  78. X
  79. Xprofm.obj:    profile.c
  80. X    $(CC) $(CFLAGS) -Foprofm.obj profile.c
  81. END_OF_makefile.msc
  82. if test 447 -ne `wc -c <makefile.msc`; then
  83.     echo \"makefile.msc\" unpacked with wrong size!
  84. fi
  85. # end of overwriting check
  86. fi
  87. if test -f profile.c -a "${1}" != "-c" ; then 
  88.   echo Will not over-write existing file \"profile.c\"
  89. else
  90. echo Extracting - \"profile.c\"
  91. sed "s/^X//" >profile.c <<'END_OF_profile.c'
  92. X/****************************************************************/
  93. X/*               PROFILE.C                */
  94. X/*                                */
  95. X/* Execution time profiler. Reads an executable and it's link    */
  96. X/* map and produces an output file with hit counts for all the    */
  97. X/* functions listed in the map file. Handles (by option selec-    */
  98. X/* tion) both MicroSoft LINK and Borland TLINK map files.    */
  99. X/* PROF only profiles '.EXE' files.                */
  100. X/****************************************************************/
  101. X/* Command line:                        */
  102. X/*                                */
  103. X/* prof [-adin0$?] [-#<n>] [-m<mapfile>] [-o<outfile>] <cmd>    */
  104. X/*                                */
  105. X/* -d    Include DOS areas (DOS and BIOSes) in the profiling.    */
  106. X/* -a    Sort output table by address, not by frequency.        */
  107. X/* -i    Include intrinsic functions (name starting with '__,    */
  108. X/*    or containing '@' or '$')' in the profiling.        */
  109. X/* -n    Sort output table by name, not by frequency.        */ 
  110. X/* -0    List also functions that got no hits.            */
  111. X/* -#<n>                            */
  112. X/*    Execute the profiled command <n> times to increase    */
  113. X/*    statistical confidence.                    */
  114. X/* -?    Report the address of the own PSP and  main() function.    */
  115. X/* -$    Report the address of the top DOS aloccation.        */ 
  116. X/* -m<mapfile>                            */
  117. X/*    Read link map from file <mapfile> (default <cmd>.MAP).    */
  118. X/* -o<outfile>                            */
  119. X/*    Output profile table in file <outfile> (default        */
  120. X/*    <cmd.PRF>).                        */
  121. X/* <cmd>                            */
  122. X/*    The normal command line for the profiled command.    */
  123. X/****************************************************************/
  124. X/* Exit codes delivered by prof:                */
  125. X/*                                */
  126. X/*  0:    No error.                        */
  127. X/*  1:    Illegal command line arguments.                */
  128. X/*  2:    Cannot open command binary.                */
  129. X/*  3:    Cannot open linker map file.                */
  130. X/*  4:    Cannot open output file.                */
  131. X/*  5:    Invalid map file format.                */
  132. X/*  6:    Insufficient memory.                    */
  133. X/*  7:    Invalid EXE file format.                */
  134. X/*  8:    EXEC error                        */
  135. X/*  9:    Program too fast - no hits.                */
  136. X/****************************************************************/
  137. X/* Revised:                            */
  138. X/* 1.02: Doesn't show functions that were not hit, thus        */
  139. X/*     reducing the amount of output data:        890909    */
  140. X/* 1.01: Attempts to make load address more certain:    890906    */
  141. X/* 1.00: Functional:                    890904    */
  142. X/****************************************************************/
  143. X#include <stdio.h>
  144. X#include <stdlib.h>
  145. X#include <string.h>
  146. X#include <ctype.h>
  147. X#include <io.h>
  148. X#include <fcntl.h>
  149. X#include <sys/types.h>
  150. X#include <sys/stat.h>
  151. X#include <process.h>
  152. X#include <dos.h>
  153. X#include <errno.h>
  154. X#if TRC_2_0
  155. X#include <alloc.h>
  156. X#endif
  157. X#if MSC_5_1
  158. X#include <malloc.h>
  159. X#include <signal.h>
  160. X#endif
  161. X
  162. X/****************************************************************/
  163. X/*          Adjuct constant for file load addresses.        */
  164. X/*   THIS IS COMPILER SPECIFIC AND MUST BE FOUND EMPIRICALLY!    */
  165. X/****************************************************************/
  166. X
  167. X#if TRC_2_0
  168. X#define    ADJ_CONST    0L
  169. X#endif
  170. X#if MSC_5_1
  171. X#define    ADJ_CONST    0x160L
  172. X#endif
  173. X
  174. X/* Compiler-specific #defines for DOS access functions */
  175. X
  176. X#if TRC_2_0
  177. X#define    ENABLE()    enable()
  178. X#define    DISABLE()    disable()
  179. X#define    ALLMEM(s,p,r)    (r = allocmem(0xffff,p))
  180. X#define    ALLMEMERR(s,p)    (allocmem(s,p) != -1)
  181. X#define    FREEMEM(p)    freemem(p)
  182. X#define    CTRLBREAK(f)    ctrlbrk(f)
  183. X#define    SETVECT(i,f)    setvect(i,f)
  184. X#define    GETVECT(i)    getvect(i)
  185. X#endif
  186. X#if MSC_5_1
  187. X#define    ENABLE()    _enable()
  188. X#define    DISABLE()    _disable()
  189. X#define    ALLMEM(s,p,r)    (_dos_allocmem(0xffff,&r))
  190. X#define    ALLMEMERR(s,p)    (_dos_allocmem(s,p) != 0)
  191. X#define    FREEMEM(p)    _dos_freemem(p)
  192. X#define    CTRLBREAK(f)    signal(SIGINT,f)
  193. X#define    SETVECT(i,f)    _dos_setvect(i,f)
  194. X#define    GETVECT(i)    _dos_getvect(i)
  195. X#endif
  196. X
  197. X#define    TMRINT    8                /* Timer hardware interrupt */
  198. X#define    MAXFUNC 2000                /* Max 2000 functions... */
  199. X#define    CMDSIZ    130                /* Cmd's command line size */
  200. X#define    FNSIZ    130                /* Max file name size */
  201. X#define    LNSIZ    130                /* Max map file line length */
  202. X#define    STSIZ    35                /* Size of small strings */
  203. X
  204. X#define    ABSTYP    1                /* Table entry is absolute */
  205. X#define    USRTYP    2                /* Table entry is USR func */
  206. X#define    INRTYP    4                /* Table entry is INR type */
  207. X#define    DOSTYP    8                /* Table entry is DOS type */
  208. X
  209. Xtypedef struct                    /* Entry in funcion table */
  210. X  {
  211. X  unsigned long addr;                /* Function start address */
  212. X  unsigned long hits;                /* Hit count */
  213. X  char           *name;                /* Function name */
  214. X  char        typ;                /* Function properties */
  215. X  char        dummy;                /* For word-aligning */
  216. X  } fdesc;                    /* Function descriptor */
  217. X
  218. Xstatic    char    exename[FNSIZ]  = {0};        /* Prof'ed command's binary */
  219. Xstatic    char    mapname[FNSIZ]  = {0};        /* Prof'ed command's map */
  220. Xstatic    char    outname[FNSIZ]  = {0};        /* Prof output table file */
  221. X
  222. Xstatic    FILE   *file = NULL;            /* For read/write of files */
  223. Xstatic    struct stat statinfo;            /* Stat() buffer */
  224. Xstatic    fdesc  *descs;                /* Start of desc table */
  225. Xstatic    int    exe_count = 1;            /* # times to exec command */
  226. Xstatic    int    nfuncs;                /* Number of functions */
  227. Xstatic    char    name_ordr = 0;            /* If sorting address-wise */
  228. Xstatic    char    addr_ordr = 0;            /* If sorting adress.-wise */
  229. Xstatic    char    tell_psp = 0;            /* Tell load PSP address */
  230. Xstatic    char    list_nulls = 0;            /* IF listing no-hit funcs */
  231. Xstatic    char    wrt_msk = USRTYP;        /* Default only user funcs */
  232. Xstatic    double    tot_hits = 0.0;            /* Total function hits */
  233. Xstatic    double    dsp_hits = 0.0;            /* Total displayed hits */
  234. Xstatic    double    usr_hits = 0.0;            /* Total user pgm hits */
  235. Xstatic    char  **cmdline;            /* Command line array ptr */
  236. Xstatic    unsigned freemem_pg = 0;        /* At beg of free memory */
  237. Xstatic    unsigned load_psp = 0;            /* Paragr of load area */
  238. X
  239. Xstatic void (interrupt *old_tmr_handler)() = NULL; /* Original handler */
  240. X
  241. Xstatic    char   *msgs[] =
  242. X  {
  243. X  "profile: ",                    /* 0 */
  244. X  "Illegal command line option: `-%s'\n",    /* 1 */
  245. X  "No command specified for profiling\n",    /* 2 */
  246. X  "Cannot find executable `%s'\n",        /* 3 */
  247. X  "`%s' is a directory\n",            /* 4 */
  248. X  "Cannot find linker map file `%s'\n",        /* 5 */
  249. X  "Output file `%s' is write-protected\n",    /* 6 */
  250. X  "Invalid map file format in `%s'",        /* 7 */
  251. X  "Insufficient memory for function tables\n",    /* 8 */
  252. X  "Not enough function table slots\n",        /* 9 */
  253. X  "Cannot open output file `%s'\n",        /* 10 */
  254. X  "Insufficient memory to execute command",    /* 11 */
  255. X  "Error executing command %s\n",        /* 12 */
  256. X  "Invalid EXE file format in `%s'\n",        /* 13 */
  257. X  "@(#)profile.c v.1.02 - 890909",        /* 14 */
  258. X  "Program ran too fast - no hits!\n"        /* 15 */
  259. X  } ; /* msgs */
  260. X
  261. Xstatic    void    get_cmdline();            /* Interpret command line */
  262. Xstatic    void    set_filenames();        /* Fix definitive filenames */
  263. Xstatic    void    check_files();            /* Check files are OK */
  264. Xstatic    void    read_map();            /* Read map, build tables */
  265. Xstatic    void    add_func();            /* add function to table */
  266. Xstatic    int    blankline();            /* Check if line is blank */
  267. Xstatic    void    adjust();            /* Set correct func addrs */
  268. Xstatic    void    profile();            /* Do the actual profiling */
  269. Xstatic    void    write_result();            /* Output result */
  270. Xstatic    int    fadr_comp();            /* Compare func addresses */
  271. Xstatic    int    fhit_comp();            /* Compare func hitcounts */
  272. Xstatic    int    fnam_comp();            /* Compare func names */
  273. Xstatic    void    usage();            /* Help routine */
  274. Xstatic    void    error();            /* Error/Abort routine */
  275. X
  276. Xstatic    int    brk_handler();            /* SIGINT handler */
  277. Xstatic    void    interrupt tmr_handler();    /* Timer interrupt handler */
  278. X
  279. X/****************************************************************/
  280. X
  281. Xvoid    main(narg, args)
  282. X  int    narg;
  283. X  char *args[];
  284. X  {
  285. X  CTRLBREAK(brk_handler);
  286. X  get_cmdline(narg, args);
  287. X  set_filenames();
  288. X  check_files();
  289. X  read_map();
  290. X  adjust();
  291. X  profile();
  292. X  write_result();
  293. X  error(0,"");
  294. X  } /* main */
  295. X
  296. X/****************************************************************/
  297. X/* Get_cmdline() extracts all switches etc, gets the name of    */
  298. X/* the command to be profiled, and sets up file names. It also    */
  299. X/* builds the command line for the command to be profiled.    */
  300. X/****************************************************************/
  301. X
  302. Xstatic    void    get_cmdline(narg, args)
  303. X  int          narg;
  304. X  char         *args[];
  305. X  {
  306. X  int          i = 1;
  307. X  char         *p;
  308. X  unsigned long   m;
  309. X
  310. X  while ((i < narg) && (*args[i] == '-'))    /* Extract switches */
  311. X    {
  312. X    args[i]++;
  313. X    while (*args[i])                /* Get switches */
  314. X      {
  315. X      switch (*args[i])
  316. X    {
  317. X    case '#':   if (sscanf(args[i]+1, "%d", &exe_count) != 1)
  318. X              error(1, msgs[1], args[i]);
  319. X            args[i] = " ";
  320. X            break;
  321. X    case '?':   printf("Actual PSP is at absolute address 0x%05x0\n",
  322. X                            _psp);
  323. X            p = (char *) main;        /* Trick for MSC */
  324. X            m = FP_SEG(p);
  325. X            m <<= 4;
  326. X            m += FP_OFF(p);
  327. X            printf("Main() fnc is at absolute address 0x%06lx\n",
  328. X                            m);
  329. X            error(0,"");
  330. X    case '$':   tell_psp = 1;        /* Tell DOS alloc address */
  331. X            break;
  332. X    case 'a':   name_ordr = 0;        /* Sort table by freq */
  333. X            addr_ordr = 1;
  334. X            break;
  335. X    case 'd':   wrt_msk |= DOSTYP;        /* Include DOS areas */
  336. X            break;
  337. X    case 'i':   wrt_msk |= INRTYP;        /* Include intrinsic funcs */
  338. X            break;
  339. X    case 'n':   name_ordr = 1;        /* Sort table by name */
  340. X            addr_ordr = 0;
  341. X            break;
  342. X    case '0':   list_nulls = 1;        /* List no-hit funcs */
  343. X            break;
  344. X    case 'm':   strcpy(mapname, args[i]+1);    /* Map file name */
  345. X            args[i] = " ";
  346. X            break;
  347. X    case 'o':   strcpy(outname, args[i]+1);    /* Output table file name */
  348. X            args[i] = " ";
  349. X            break;
  350. X    default:    error(1, msgs[1], args[i]);
  351. X    } /* switch */
  352. X      args[i]++;
  353. X      } /* while */
  354. X    i++;
  355. X  } /* while */
  356. X
  357. X  if (i >= narg)                /* Check there is a command */
  358. X    error(1, msgs[2]);
  359. X  strcpy(exename,args[i]);
  360. X  cmdline = args + i;
  361. X  } /* get_cmdline */
  362. X
  363. X/****************************************************************/
  364. X/* Set_filenames() adjust names of needed file names based on    */
  365. X/* defaults and options.                    */
  366. X/****************************************************************/
  367. X
  368. Xstatic    void    set_filenames()
  369. X  {
  370. X  if (!mapname[0])                /* Set default mapfile name */
  371. X    {
  372. X    strcpy(mapname, exename);
  373. X    strcat(mapname, ".map");
  374. X    } /* if */
  375. X  if (!outname[0])                /* Set default outfile name */
  376. X    {
  377. X    strcpy(outname, exename);
  378. X    strcat(outname, ".prf");
  379. X    } /* if */
  380. X  strcat(exename,".exe");            /* It's an 'EXE' file */
  381. X  } /* set_filenames */
  382. X
  383. X/****************************************************************/
  384. X/* Check_files() checks that all files are available, readable    */
  385. X/* and writeable as required.                    */
  386. X/****************************************************************/
  387. X
  388. Xstatic    void    check_files()
  389. X  {
  390. X  if (stat(exename, &statinfo))            /* Check executable exists */
  391. X    error(2, msgs[3], exename);
  392. X  if (statinfo.st_mode & S_IFDIR)
  393. X    error(2, msgs[4], exename);
  394. X  if (stat(mapname, &statinfo))            /* Check mapfile exists */
  395. X    error(3, msgs[5], mapname);
  396. X  if (statinfo.st_mode & S_IFDIR)
  397. X    error(3, msgs[4], mapname);
  398. X  if (stat(outname, &statinfo))            /* Check outfile writeable */
  399. X    return;
  400. X  if (statinfo.st_mode & S_IFDIR)
  401. X    error(4, msgs[4], outname);
  402. X  if (!(statinfo.st_mode & S_IWRITE))
  403. X    error(4, msgs[6], outname);
  404. X  } /* check_files */
  405. X
  406. X/****************************************************************/
  407. X/* Read_map() reads the map file into memory and builds the    */
  408. X/* linked list of entries.                    */
  409. X/****************************************************************/
  410. X
  411. Xstatic    void    read_map()
  412. X  {
  413. X  char     line[LNSIZ+1];
  414. X  char     str1[STSIZ],str2[STSIZ],str3[STSIZ],str4[STSIZ];
  415. X  fdesc *p;
  416. X  long     ofs, seg;
  417. X
  418. X  if ((p = descs = calloc(MAXFUNC,sizeof(fdesc))) == NULL)
  419. X    error(6, msgs[8]);
  420. X
  421. X  if ((file = fopen(mapname,"r")) == NULL)    /* 'Impossible' */
  422. X    error(3, msgs[5], mapname);
  423. X
  424. X  while (fgets(line, LNSIZ, file) != NULL)    /* Find 'Add Pub by Val' */
  425. X    {
  426. X    if (
  427. X         (sscanf(line, " %s %s %s %s ", str1, str2, str3, str4) == 4)
  428. X       && 
  429. X     (stricmp(str1, "Address") == 0)
  430. X       && 
  431. X     (stricmp(str2, "Publics") == 0)
  432. X       && 
  433. X     (strcmp(str3, "by") == 0)
  434. X       && 
  435. X     (strcmp(str4, "Value") == 0)
  436. X       )
  437. X      break;
  438. X    } /* while */
  439. X  if (feof(file))
  440. X    error(5, msgs[7], mapname);
  441. X
  442. X  while (fgets(line, LNSIZ, file) != NULL)    /* Find Non-blank line */
  443. X    if (!blankline(line))
  444. X      break;
  445. X  if (feof(file))
  446. X    error(5, msgs[7], mapname);
  447. X
  448. X  add_func(p++, "Low Mem", 0l, ABSTYP|DOSTYP);    /* Make entry for low mem */
  449. X  add_func(p++, "DOS", 0x400l, ABSTYP|DOSTYP);    /* Make entry for low mem */
  450. X  seg = _psp;                    /* Get profiler's psp */
  451. X  add_func(p++,"Profiler",seg<<4,ABSTYP|DOSTYP);/* Make entry for prof */
  452. X  
  453. X  nfuncs = 2;
  454. X  do                        /* Process and read another */
  455. X    {
  456. X    if (blankline(line))            /* Blank line end of data */
  457. X      break;
  458. X    if (sscanf(line, " %lx:%lx Abs %s ", &seg, &ofs, str1) != 3)
  459. X      if (sscanf(line, " %lx:%lx    %s ", &seg, &ofs, str1) != 3)
  460. X    error(5, msgs[7], mapname);
  461. X    if (str1[0] == '_')                /* Play with '_' for */
  462. X      str1[0] = 1;                /* alpha sorting */
  463. X    if (str1[1] == '_')                /* This is converted back */
  464. X      str1[1] = 0x7f;                /* on output */
  465. X
  466. X    if (                    /* Intrinsic function */
  467. X         ((str1[0] == 1) && (str1[1] == 0x7f))    /* with '__' */
  468. X       ||
  469. X         (strchr(str1,'@') != NULL)        /* or with '@' */
  470. X       ||
  471. X         (strchr(str1,'$') != NULL)        /* or with '$' */
  472. X       )
  473. X      add_func(p++, str1, (seg << 4) + ofs, INRTYP);/* Make entry */
  474. X    else                    /* User function */
  475. X      add_func(p++, str1, (seg << 4) + ofs, USRTYP);/* Make entry */
  476. X    nfuncs++;
  477. X    if (nfuncs > (MAXFUNC - 10))
  478. X      error(6, msgs[9]);
  479. X    }
  480. X  while (fgets(line, LNSIZ, file) != NULL);
  481. X
  482. X  add_func(p++,"EGA BIOS", 0xc0000l, ABSTYP|DOSTYP);
  483. X  add_func(p++,"Fixed Disk BIOS", 0xc8000l, ABSTYP|DOSTYP);
  484. X  add_func(p++,"System ROM", 0xf0000l, ABSTYP|DOSTYP);
  485. X  add_func(p++,"System BIOS", 0xfe000l, ABSTYP|DOSTYP);
  486. X  nfuncs += 4;
  487. X
  488. X  fclose(file);
  489. X  file = (FILE *) NULL;
  490. X  } /* read_map */
  491. X
  492. X/****************************************************************/
  493. X/* Add_func() adds a function to the function table.        */
  494. X/****************************************************************/
  495. X
  496. Xstatic    void    add_func(p, nam, addr, typ)
  497. X  fdesc    *p;
  498. X  char    *nam;
  499. X  long     addr;
  500. X  char     typ;
  501. X  {
  502. X  p->addr = addr;
  503. X  p->hits = 0l;
  504. X  if ((p->name = calloc(1,strlen(nam)+1)) == NULL)
  505. X    error(6, msgs[8]);
  506. X  strcpy(p->name, nam);
  507. X  p->typ = typ;
  508. X  } /* add_func */
  509. X
  510. X/****************************************************************/
  511. X/* Blankline() returns 1 if the passed line is entirely blank.    */
  512. X/****************************************************************/
  513. X
  514. Xstatic    int    blankline(s)
  515. X  char    *s;
  516. X  {
  517. X  while (*s)
  518. X    {
  519. X    if (!isspace(*s))
  520. X      return(0);
  521. X    s++;
  522. X    } /* while */
  523. X  return(1);
  524. X  } /* blankline */
  525. X
  526. X/****************************************************************/
  527. X/* Adjust() finds out where in memory the executable will be    */
  528. X/* loaded, and adjust function addresses accordingly. Does this    */
  529. X/* By allocating first a hole, then sys memory 1, and then sys    */
  530. X/* memory 2. Sys memory 2 indicates first free address, and is    */
  531. X/* immediately re-freed. The hole is also freed to function as    */
  532. X/* work space for functions that will run before the actual    */
  533. X/* execution of the profiled program. THIS IS TRICKY AND COM-    */
  534. X/* PILER DEPENDENT...                        */
  535. X/****************************************************************/
  536. X
  537. Xstatic    void    adjust()
  538. X  {
  539. X  char      *hole;
  540. X  long       adj;
  541. X  int       i;
  542. X  int       maxsz;
  543. X
  544. X  if ((hole = malloc(0x800)) == NULL)        /* Fix workspace for others */
  545. X    error(6, msgs[11]);
  546. X  if (ALLMEMERR(0x20, &freemem_pg))        /* Grab small mem over it */
  547. X    error(6, msgs[11]);
  548. X  ALLMEM(0xffff, &load_psp, maxsz);        /* See what max space is */
  549. X  if (ALLMEMERR(maxsz, &load_psp))        /* Grab it to know address */
  550. X    error(6, msgs[11]);
  551. X  free (hole);                    /* Make workspace available */
  552. X
  553. X  adj = load_psp;
  554. X  adj <<= 4;
  555. X  adj += 0x100 + ADJ_CONST;
  556. X
  557. X  if (tell_psp)                    /* If display free start */
  558. X    printf("Expecting PSP at absolute address 0x%06lx\n", adj-0x100L);
  559. X       
  560. X  for (i = 0; i < nfuncs; i++)            /* Add adj to func addr:s */
  561. X    if (!((descs + i)->typ & ABSTYP))        /* Only relocatable ones */
  562. X      (descs + i)->addr += adj;
  563. X
  564. X  qsort(descs,nfuncs,sizeof(fdesc),fadr_comp);    /* Sort in address order */
  565. X  } /* adjust */
  566. X
  567. X/****************************************************************/
  568. X/* Profile() does the profiling. It finds out where the pro-    */
  569. X/* filed command will be loaded, adjusts function addresses    */
  570. X/* accordingly, starts timer interrupts, and executes the com-    */
  571. X/* mand as a subshell.                        */
  572. X/****************************************************************/
  573. X
  574. Xstatic    void    profile()
  575. X  {
  576. X  int    i = 0;
  577. X
  578. X  old_tmr_handler = GETVECT(TMRINT);        /* Save old int vector */
  579. X  SETVECT(TMRINT,tmr_handler);            /* Start profiling */
  580. X  DISABLE();
  581. X  FREEMEM(load_psp);                /* Free the load area */
  582. X  load_psp = 0;                    /* To not free it at exit */
  583. X  ENABLE();
  584. X  while(i++ < exe_count)
  585. X    {
  586. X    fprintf(stderr, "%-3d ----- Executing %s\n", i, *cmdline);
  587. X    if (spawnv(P_WAIT, *cmdline, cmdline) == -1)
  588. X      {
  589. X      switch(errno)
  590. X    {
  591. X    case ENOEXEC:    error(7,msgs[13], exename);
  592. X            break;
  593. X        case ENOMEM:    error(6,msgs[11]);
  594. X            break;
  595. X        default:        error(8,msgs[12]);
  596. X        } /* switch */
  597. X      } /* if */
  598. X    } /* switch */
  599. X
  600. X  SETVECT(TMRINT,old_tmr_handler);
  601. X  (char *) old_tmr_handler = NULL;
  602. X
  603. X  DISABLE();
  604. X  FREEMEM(freemem_pg);
  605. X  freemem_pg = 0;
  606. X  ENABLE();
  607. X  fprintf(stderr, "--------- Executing completed\n");
  608. X  } /* profile */
  609. X
  610. X/****************************************************************/
  611. X/* Write_result() sorts the data, computes profiling percen-    */
  612. X/* tages, and write out the resulting list.            */
  613. X/****************************************************************/
  614. X
  615. Xstatic    void    write_result()
  616. X  {
  617. X  int     i;
  618. X
  619. X  if (name_ordr)                /* Sort table by name? */
  620. X    qsort(descs,nfuncs,sizeof(fdesc),fnam_comp);/* Sort in name order */
  621. X  else
  622. X    if (!addr_ordr)
  623. X      qsort(descs,nfuncs,sizeof(fdesc),fhit_comp);/* Sort in freq order */
  624. X
  625. X  if ((file = fopen(outname,"w")) == NULL)    /* Possible if command did */
  626. X    error(4, msgs[10], outname);        /* something like chmod -w */
  627. X
  628. X  for (i = 0; i < nfuncs; i++)            /* Add up total hit counts */
  629. X    {
  630. X    tot_hits += (double) ((descs + i)->hits);    /* Add upp total hits */
  631. X    if ((descs + i)->typ & wrt_msk)
  632. X      dsp_hits += (double) ((descs + i)->hits);    /* Add upp displayed hits */
  633. X    if ((descs + i)->typ & USRTYP)
  634. X      usr_hits += (double) ((descs + i)->hits);    /* Add up user hits */
  635. X    } /* for */
  636. X  if (tot_hits == 0.0)                /* Avoid div by 0.0 */
  637. X    tot_hits = 1.0;
  638. X  if (dsp_hits == 0.0)                /* Avoid div by 0.0 */
  639. X    {
  640. X    if (!tell_psp)
  641. X      error(9,msgs[15]);
  642. X    else
  643. X      dsp_hits = 1.0;
  644. X    } /* if */
  645. X  if (usr_hits == 0.0)                /* Avoid div by 0.0 */
  646. X    usr_hits = 1.0;
  647. X
  648. X  fprintf(file, "Function name          Addr      Total    Disp    User\n\n");
  649. X  for (i = 0; i < nfuncs; i++)
  650. X    {
  651. X    if (((descs + i)->hits == 0) &&        /* Don't show 0 hit funcs */
  652. X        !(list_nulls || tell_psp))
  653. X      continue;
  654. X    if (wrt_msk & (descs +i)->typ)
  655. X      {
  656. X      if ((descs + i)->name[0] == 1)        /* Reconvert fixes done */
  657. X    (descs + i)->name[0] = '_';        /* when reading the map */
  658. X      if ((descs + i)->name[1] == 0x7f)
  659. X    (descs + i)->name[1] = '_';
  660. X      fprintf(file, "%-20s  %05lx     %6.2f  %6.2f",
  661. X    (descs + i)->name, (descs + i)->addr,
  662. X    100.0 * ((descs + i)->hits / tot_hits),
  663. X    100.0 * ((descs + i)->hits / dsp_hits));
  664. X      if ((descs + i)->typ & USRTYP)        /* Only usrfuncs get col 3 */
  665. X    fprintf(file,"  %6.2f", 100.0 * ((descs + i)->hits / usr_hits));
  666. X      fprintf(file,"\n");
  667. X      } /* if */
  668. X    } /* for */
  669. X  fprintf(file, "\nStatistics based on %6.0f hits\n", tot_hits);
  670. X  fclose(file);
  671. X  file = (FILE *) NULL;
  672. X  } /* write_result */
  673. X
  674. X/****************************************************************/
  675. X/* Fadr_comp() compares addresses of two functions. If address    */
  676. X/* values are the same, name decides.                */
  677. X/****************************************************************/
  678. X
  679. Xstatic    int    fadr_comp(f1, f2)
  680. X  fdesc    *f1, *f2;
  681. X  {
  682. X  if (f1->addr > f2->addr)
  683. X    return(1);
  684. X  if (f1->addr < f2->addr)
  685. X    return(-1);
  686. X  return(fnam_comp(f1,f2));
  687. X  } /* fadr_comp */
  688. X
  689. X/****************************************************************/
  690. X/* Fhit_comp() compares hit counts of two function table    */
  691. X/* entries. If counts are the same, name decides.        */
  692. X/****************************************************************/
  693. X
  694. Xstatic    int    fhit_comp(f1, f2)
  695. X  fdesc    *f1, *f2;
  696. X  {
  697. X  if (f1->hits > f2->hits)
  698. X    return(-1);
  699. X  if (f1->hits < f2->hits)
  700. X    return(1);
  701. X  return(fnam_comp(f1,f2));
  702. X  } /* fhit_comp */
  703. X
  704. X/****************************************************************/
  705. X/* Fnam_comp() compares names of two function table entries.    */
  706. X/****************************************************************/
  707. X
  708. Xstatic    int    fnam_comp(f1, f2)
  709. X  fdesc    *f1, *f2;
  710. X  {
  711. X  return (stricmp(f1->name, f2->name));
  712. X  } /* fnam_comp */
  713. X
  714. X/****************************************************************/
  715. X/* Usage() displays a usage menu.                */
  716. X/****************************************************************/
  717. X
  718. Xstatic    void    usage()
  719. X  {
  720. X  fprintf(stderr,
  721. X "Usage: profile [-adin0] [-#<n>] [-m<map>] [-o<out>] <cmd> [<args>...]\n");
  722. X  fprintf(stderr,
  723. X "       -a       Sort output by adress, not by frequency\n");
  724. X  fprintf(stderr,
  725. X "       -d       Include DOS and BIOS areas in profiling\n");
  726. X  fprintf(stderr,
  727. X "       -i       Include intrinsic functions (starting with `__', or\n");
  728. X  fprintf(stderr,
  729. X "                containing the characters `@' or '$') in profiling\n");
  730. X  fprintf(stderr,
  731. X "       -n       Sort output by name, not by frequency\n");
  732. X  fprintf(stderr,
  733. X "       -0       List also functions that had zero frequency\n");
  734. X  fprintf(stderr,
  735. X "       -#<n>    Execute profiled command <n> times (default 1)\n");
  736. X  fprintf(stderr,
  737. X "       -m<map>  Use file <map> as linker map info (default <cmd>.MAP)\n");
  738. X  fprintf(stderr,
  739. X "       -o<out>  Put output result in file <out> (default <cmd>.PRF)\n");
  740. X  fprintf(stderr,
  741. X "       <cmd>    Name of command to be profiled (including path)\n");
  742. X  fprintf(stderr,
  743. X "       <args>   Optional arguments to <cmd>\n");
  744. X  } /* usage */
  745. X
  746. X/****************************************************************/
  747. X/* Error()                            */
  748. X/*                                */
  749. X/* Print an error diagnosis and exit.                */
  750. X/****************************************************************/
  751. X/*VARARGS*/
  752. Xstatic    void error(ercode, ermsg, s1, s2, s3, s4)
  753. X  int     ercode;
  754. X  char    *ermsg;
  755. X  char    *s1, *s2, *s3, *s4;
  756. X  {
  757. X  if ((char *) old_tmr_handler != NULL)
  758. X    SETVECT(TMRINT,old_tmr_handler);
  759. X  if (freemem_pg)
  760. X    FREEMEM(freemem_pg);
  761. X  if (load_psp)
  762. X    FREEMEM(load_psp);
  763. X  if (ercode != 0)
  764. X    {
  765. X    fprintf(stderr, msgs[0]);
  766. X    fprintf(stderr, ermsg, s1, s2, s3, s4);
  767. X    } /* if */
  768. X  if (ercode == 1)
  769. X    usage();
  770. X  if (file != (FILE *) NULL)
  771. X    fclose(file);
  772. X  exit(ercode);
  773. X  } /* error */
  774. X
  775. X/****************************************************************/
  776. X/* Brk_handler() catches CTRL-C interrupts.            */
  777. X/****************************************************************/
  778. X
  779. Xstatic    int    brk_handler()
  780. X  {
  781. X  CTRLBREAK(brk_handler);
  782. X  error(255,"User abort\n");
  783. X  return(0);                    /* Actually not executed */
  784. X  } /* brk_handler */
  785. X
  786. X/****************************************************************/
  787. X/* Tmr_handler() is the interrupt handler that updates hit    */
  788. X/* counts. It must be fast.                    */
  789. X/****************************************************************/
  790. X
  791. X#if TRC_2_0
  792. Xstatic    void interrupt tmr_handler(bp,di,si,ds,es,dx,cx,bx,ax,ip,cs)
  793. X  unsigned bp,di,si,ds,es,dx,cx,bx,ax,ip,cs;
  794. X#endif
  795. X#if MSC_5_1
  796. Xstatic    void interrupt tmr_handler(es,ds,di,si,bp,sp,bx,dx,cx,ax,ip,cs)
  797. X  unsigned es,ds,di,si,bp,sp,bx,dx,cx,ax,ip,cs;
  798. X#endif
  799. X  {
  800. X  long    addr;
  801. X  int    lower, upper, middle;
  802. X
  803. X  addr = ((unsigned long)cs << 4) + (unsigned long) ip;
  804. X  lower = 0;
  805. X  upper = nfuncs - 1;
  806. X
  807. X  while (upper - lower > 1)
  808. X    {
  809. X    middle = (lower + upper) / 2;
  810. X    if ((descs + middle)->addr <= addr)
  811. X      lower = middle;
  812. X    else
  813. X      upper = middle;
  814. X    } /* while */
  815. X  (descs + lower)->hits++;
  816. X  (*old_tmr_handler)();
  817. X  } /* tmr_handler */
  818. END_OF_profile.c
  819. if test 23853 -ne `wc -c <profile.c`; then
  820.     echo \"profile.c\" unpacked with wrong size!
  821. fi
  822. # end of overwriting check
  823. fi
  824. if test -f profile.man -a "${1}" != "-c" ; then 
  825.   echo Will not over-write existing file \"profile.man\"
  826. else
  827. echo Extracting - \"profile.man\"
  828. sed "s/^X//" >profile.man <<'END_OF_profile.man'
  829. XPROFILE(1)        MS-DOS Programmer's Manual         PROFILE(1)
  830. X
  831. XNAME
  832. X   profile - execution time statistics collector
  833. X
  834. XSYNOPSIS
  835. X   profile [-adin0] [-#<n>] [-m<mapfile>] [-o<outfile>] <cmdline>
  836. X
  837. XDESCRIPTION
  838. X   Profile(1) executes a DOS command and gathers information about where in
  839. X   the program the execution time is spent. After the command has terminated,
  840. X   profile(1) writes a report file where the data is presented.
  841. X
  842. X   Current implementations of profile(1) requires that the program under test
  843. X   is linked with the MicroSoft LINK or the Borland TLINK linkers, and that a
  844. X   standard format map file (as produced by the above linkers) was generated.
  845. X   The link command for TLINK must contain the options '/l/c', and for the MS
  846. X   LINK linker, the '/MAP' option must be specified.
  847. X
  848. X   Normally, profile(1) assumes a linker map file with the same name as the
  849. X   executable file (including any pathes) but with the extension '.MAP' in-
  850. X   stead of '.EXE'. By default the output file has the same name as the '.EXE'
  851. X   file, but with extension '.PRF'. Both these conventions can be overridden.
  852. X
  853. X   The profiler will present it's output data summary based on the information
  854. X   about public function names and addresses described by the linker map file.
  855. X   If the map file is out of date, profiling results may be incorrect. Fur-
  856. X   thermore, the profiler only displays public functions. One way to ensure
  857. X   that interesting symbols in 'C' programs are public is to temporarily in-
  858. X   sert the line
  859. X
  860. X       #define    static
  861. X
  862. X   at the head of all interesting 'C' modules (this may in some cases cause
  863. X   multiple definition errors in the link stage, if different functions and
  864. X   variables have the same names in different modules).
  865. X
  866. X   In cases where lengthy execution of a command is needed to produce relia-
  867. X   ble statistics, an option is available to make the profiled command run
  868. X   multiple times.
  869. X
  870. X   Profile(1) will not process '.COM' files.
  871. X
  872. XINTERNAL FUNCTIONING
  873. X   Profile(1) works by first reading the linker map file and collect all the
  874. X   function name and address information in memory. It then find out where in
  875. X   the physical memory the executable will be loaded, and adjusts table infor-
  876. X   mation according to this. Next, it installs it's own handler function for
  877. X   for the PC real-time clock interrupt, and loads and executes the program.
  878. X   Each time a real-time clock interrupt occurs, the interrupt handler will
  879. X   record in which function the CPU was executing. This continues until the
  880. X   profiled program terminates. Profile(1) then uninstalls the interrupt
  881. X   handler and formats and outputs the results.
  882. X
  883. X   All functions described in the map file are classified either as 'intrinsic
  884. X   functions' or 'user functions', according to their names: intrinsic func-
  885. X   tions are those whose names start with '__', or which contain the charac-
  886. X   ters '@' or '$'. All other functions are considered user functions. Apart
  887. X   from the functions described in the map file, other interesting memory
  888. X   areas are also defined:
  889. X
  890. X       'Low Mem'       from 0 to 0x3ff
  891. X    'DOS'          from 0x400 to the beginning od the profile itself
  892. X    'Profiler'      from the beginning to the end of the profiler
  893. X    'EGA BIOS'      from 0xc0000 to 0xc7fff
  894. X    'Fixed Disk BIOS' from 0xc8000 to 0xeffff
  895. X    'System ROM'      from 0xf0000 to 0xfe000
  896. X    'System BIOS'      from 0xfe000 to 0xfffff
  897. X
  898. XOUTPUT LISTING FORMAT
  899. X   By default, DOS areas and intrinsic functions will not be listed in the
  900. X   output report (this can be overridden). Below is an excerpt of a typical
  901. X   report:
  902. X
  903. X   Function name          Addr      Total    Disp    User
  904. X
  905. X   _fclose               41b1c       0.32   50.00   50.00
  906. X   _daylight             4a41e       0.00    3.00    3.00
  907. X   _edata                4a826       0.00    3.00    3.00
  908. X   _end                  4ab60       0.00    3.00    3.00
  909. X   _exit                 416fa       0.00    2.00    2.00
  910. X   _flushall             41c02       0.00    2.00    2.00
  911. X   _fopen                41c46       0.00    1.00    1.00
  912. X   _fprintf              41c7e       0.00    0.50    0.50
  913. X   _main                 402e0       0.00    0.50    0.50
  914. X     ...                  ...         ...     ...     ...
  915. X   _timezone             4a41a       0.00    0.40    0.40
  916. X   _tzname               4a420       0.00    0/30    0.30
  917. X   FIARQQ                50112       0.00    0.10    0.10
  918. X   FICRQQ                41112       0.00    0.01    0.01
  919. X
  920. X   Statistics based on    309 hits
  921. X
  922. X   As can be seen, functions are sorted according to their frequency of occu-
  923. X   rence, and secondary by their name. They can also be sorted primarily ac-
  924. X   cording to name or execution address. The first column gives name of the
  925. X   function, and the second it's address at this particular instance of execu-
  926. X   tion. Next is the percentage of each function related to total time, i.e.
  927. X   if one adds up all DOS, intrinsic and user function percentages in this
  928. X   column, the result will be 100. If DOS and intrinsics are not listed, the
  929. X   total will be less than 100%.
  930. X
  931. X   In the second column, the percentages relative to all DISPLAYED functions
  932. X   are shown. In the third, the percentages relative to all USER functions
  933. X   are listed. The two latter columns should always add up very closely to
  934. X   100% (the sum may not be exact due to rounding errors).
  935. X
  936. X   Functions that did not get any hits are not displayed by default. However,
  937. X   you can force them to appear in the output listing by giving the '-0'
  938. X   option in the command line.
  939. X
  940. X   In the last line is shown the total number of real-time clock interrupts
  941. X   that occured during profiling. This value should be as large as possible,
  942. X   otherwise it means the statistics is based on too little input data. See
  943. X   the '#' option below to make multiple runs of the same command to increase
  944. X   the statistical reliability of the data.
  945. X
  946. X   In the example above, it can be seen that of the displayed functions, half
  947. X   the time (50%) was spent in the _fclose function. Since _fclose is also
  948. X   classified as a user function, the value is the same in the third column.
  949. X   But only 0.32% of the total time was spent in _fclose, indicating that most
  950. X   of the program's execution time is spent doing system calls into DOS or
  951. X   other system resources. To find out, one could include DOS and intrinsic
  952. X   functions in the output listing by using the '-d' and/or '-i' options.
  953. X
  954. XOPTIONS
  955. X   -a        Sort the output list in order of execution address, and not
  956. X           by frequency.
  957. X   -d        Include DOS and BIOS areas, and the profiler itslef, in the
  958. X           output list.
  959. X   -i        Include intrinsic functions in the output list.
  960. X   -n        Sort the output list in order of function name, and not by
  961. X           frequency.
  962. X   -0        Normally functions that did not get any hits during execution
  963. X           are not listed in the output. The '-0' option causes them to
  964. X        be listed.
  965. X   -#<n>    Execute the profiled command <n> times to increase the relia-
  966. X           bility of the statistics (default 1).
  967. X   -m<mapfile>    Read the linker map from file <mapfile>. The default is the
  968. X           same name as the executable, but with extension '.MAP'.
  969. X   -o<outfile>    Write output data to file <outfile>. The default is the same
  970. X           name as the executable, but with extension '.OUT'.It is poss-
  971. X        ible to write the data directly to 'CON' or to the printer
  972. X        device.
  973. X   <cmdline>    The normal command line for the profiled command. Note that
  974. X           profile(1) will not follow the DOS 'PATH' environment variable
  975. X        to find the executable file. If you want to profile a command
  976. X        that is in another directory, the explicit path to it must be
  977. X        supplied. But the '.exe' extension should NOT be specified.
  978. X
  979. X        Example:
  980. X
  981. X        C> profile ..\tst\tester junk <cr>
  982. X
  983. X        will profile the 'tester.exe' program in directory /..\tst'.
  984. X        Note that in this example, the map file is assumed to be in
  985. X        the '..\tst' directory too, and the profiler output file will
  986. X        also be written to that directory.
  987. X
  988. XSPECIAL OPTIONS, NOT NORMALLY USED
  989. X   -?        Report the memory address of profile(1)'s own PSP and main()
  990. X           function. Used only during development of profile(1).
  991. X   -$        Report the memory address of the first free memory (approxi-
  992. X           mately where the profiled command will be loaded). Used only
  993. X        during development of profile(1). '-$' also causes more infor-
  994. X        mation to be included in the output listing.
  995. X
  996. XDIAGNOSTICS
  997. X    Illegal command line option: `x'
  998. X    No command specified for profiling
  999. X    Cannot find executable `x'
  1000. X    `x' is a directory
  1001. X    Cannot find linker map file `x'
  1002. X    Output file `x' is write-protected
  1003. X    Invalid map file format in `x'
  1004. X    Insufficient memory for function tables
  1005. X    Not enough function table slots
  1006. X    Cannot open output file `x'
  1007. X    Insufficient memory to execute command
  1008. X    Error executing command `x'
  1009. X    Invalid EXE file format in `x'
  1010. X    Program ran too fast - no hits!
  1011. X
  1012. XBUGS
  1013. X   The method for finding out the address where the profiled command will be
  1014. X   loaded is compiler specific (to the compiler used to compile profile(1)).
  1015. X
  1016. X   It involves giving the command
  1017. X
  1018. X       C> profile -$ profile -? <cr>
  1019. X
  1020. X   These two (!) instances of profile will produce some special output. The
  1021. X   one which EXECUTES the other will show what memory address it thinks is the
  1022. X   lowest free one. The one which IS EXECUTED by the other will report the
  1023. X   address of it's own Program Segment Prefix and main() function. Naively,
  1024. X   the two values would be expected to be the same, but they are not.
  1025. X
  1026. X   The discrepancy should be added during the adjustment of function addresses
  1027. X   to actual execution values. This is a nuisance. The values may even be dif-
  1028. X   ferent if you change the source code using the same compiler, so if you mo-
  1029. X   dify profile(1) you should check the address of the real executing main()
  1030. X   function and the assumed address for it and check that the values agree
  1031. X   after each change to the sources.
  1032. X
  1033. X   It could be considered to have some kind of regular expression format, per-
  1034. X   haps read from a specification file, that tells what functions should be
  1035. X   included in or excluded from the output report.
  1036. X
  1037. X   Other bugs? No doubt!
  1038. X
  1039. XNOTES
  1040. X   The writing of this command was inspired by a profiler distributed on Use-
  1041. X   Net by Diomidis Spinellis. Although his profile is very well written and
  1042. X   performs as described, it has two shortcomings. One is that the profiled
  1043. X   program has to be modified with a call to a special function that is linked
  1044. X   into it. Second, and much more serious: if you break out of the profiled
  1045. X   program with CTRL-C, the interrupt handler will not be restored, and as
  1046. X   soon as the memory used for it is overwritten by another command, the PC
  1047. X   dies immediately.
  1048. X
  1049. X   This profile(1) does noit suffer from these problems (but possibly from
  1050. X   many others...). Although I have taken some ideas from Diomidis' version,
  1051. X   most of this one is original work. It is hereby placed in the public do-
  1052. X   main.
  1053. X
  1054. X   Author: Bjorn Larsson        uucp: bl@infovox.se
  1055. X          Ynglingagatan 5, IV
  1056. X       S-113 47 Stockholm
  1057. X       SWEDEN
  1058. END_OF_profile.man
  1059. if test 11010 -ne `wc -c <profile.man`; then
  1060.     echo \"profile.man\" unpacked with wrong size!
  1061. fi
  1062. # end of overwriting check
  1063. fi
  1064. if test -f README -a "${1}" != "-c" ; then 
  1065.   echo Will not over-write existing file \"README\"
  1066. else
  1067. echo Extracting - \"README\"
  1068. sed "s/^X//" >README <<'END_OF_README'
  1069. X            PROFILER READ-ME    
  1070. X            ================
  1071. X
  1072. XThis software is an execution time profiler for programs developed
  1073. Xwith Turbo C and Microsoft C, or any other program that is linked
  1074. Xwith Microsoft or Turbo LINK. PROFILE will read the linker's output
  1075. Xmap file, and then run the executable program N times (default 1),
  1076. Xusing the timer interrupt to gather statistical data about where
  1077. Xexecution is mostly spent. After the executable has been run, the
  1078. Xdata is formatted in different ways and sent to a file.
  1079. X
  1080. XAs is mentioned in the MAN file, there is a constant (ADJ_CONST) in
  1081. Xthe source that is dependent on the compiler used. This is a correc-
  1082. Xtion constant to compensate the actual load address for the executing
  1083. Xprogram when it resides in primary memory. The supplied values work
  1084. Xfor MicroSoft C v.5.1, and for TurboC v.2.0. Probably other values
  1085. Xneed to be used for other compilers, and maybe also for other ver-
  1086. Xsions of DOS (the supplied values work for PCDOS 3.30). Unfortunately,
  1087. XI know of no way to know for certain where a program executed by
  1088. Xspawn..() will come down into memory. On the other hand PROFILE con-
  1089. Xtains two command line switches ('-?' and '-#') to check that the
  1090. Xconstant is correct. In other words, to install this you need a
  1091. Xlittle more than just compile it and go... it works well in my
  1092. Xinstallation. Though I'd pass it along - hopefully it is of use
  1093. Xto at least someone.
  1094. X
  1095. XA word of the program's heritage - I started to write it after
  1096. Xcollecting Diomidis Spinellis' profiler for UseNet. Although his
  1097. Xprofiler works as advertised (and is well written) I think my
  1098. Xversion is somewhat improved, as discussed in the MAN file. At
  1099. Xleast, it will handle CTRL-C in an orderly manner...
  1100. X
  1101. XTo compile, you need a UNIX compatible make (in other words,
  1102. XMicroSoft MAKE will NOT work). Anyway the makefiles are so simple
  1103. Xyou could just as well compile 'by hand'. Who knows, maybe some
  1104. Xof you can remove the need of AJD_CONST and post the result (or
  1105. Xat least mail it to me)?
  1106. X
  1107. X                            Bjorn
  1108. END_OF_README
  1109. if test 1992 -ne `wc -c <README`; then
  1110.     echo \"README\" unpacked with wrong size!
  1111. fi
  1112. # end of overwriting check
  1113. fi
  1114. echo End of shell archive.
  1115. ##  End of shell archive.
  1116. exit 0
  1117.  
  1118.  ====================== InfoVox = Speech Technology =======================
  1119.  Bjorn Larsson, INFOVOX AB      :      ...seismo!mcvax!kth!sunic!infovax!bl
  1120.  Box 2503                       :         bl@infovox.se
  1121.  S-171 02 Solna, Sweden         :         Phone (+46) 8 735 80 90
  1122.  
  1123.